/**
 *
 */
package barrier.senseBarrier;

import java.util.concurrent.atomic.AtomicInteger;

import barrier.Barrier;
import barrier.ThreadID;

/**
 * @author Yoni Seroussi
 * @author Roei Raviv
 *
 * Use an array of size n (number if threads), each cell is used to notify the
 * previous thread that this thread finished it's work for current phase
 * (thread i notifies thread i-1 for all 0<i<N)
 *
 * Padding does not help here since we update next/prev thread
 * 
 * NOTICE: this works on x86 machine, but enters deadlock on SPARC, threads 
 *  update _threadDoneArray with no lock, but there is at most only one who 
 *  writes to an element i in the array while another one is reading.
 */
public class LinearSenseBarrier implements Barrier {
  AtomicInteger count;      // how many threads have arrived   XXX should not use it.
  int _size;                // number of threads
  volatile boolean _sense;  // object's sense

  // XXX This is the diff between both impls
  boolean _threadDoneArray[]; // each thread signals next cell when it is done

  ThreadLocal<Boolean> _threadSense;
  ThreadLocal<Integer> _threadId; // thread-local sense, saves access to AtomicInt

  /**
   * Constructor
   * @param n barrier width
   */
  public LinearSenseBarrier(int n) {
    count = new AtomicInteger(n);
    _size = n;
    _sense = false;
    _threadId = new ThreadLocal<Integer>() {
      @Override
      protected Integer initialValue() {
        return ThreadID.get() % _size;
      }
    };
    _threadSense = new ThreadLocal<Boolean>() {
      @Override
      protected Boolean initialValue() { return !_sense; };
    };
    _threadDoneArray = new boolean[n];
    for (int i = (n-1) ;  i >= 0 ; i--) { // all threads not done when we start.
      _threadDoneArray[i] = false;
    }
  }

  /**
   * Wait for threads to catch up.
   */
  @Override
  public void await() {
    int me = _threadId.get();
    boolean mySense = _threadSense.get();

    if ( me == (_size - 1) ) { // im first - no need to wait for prev
      // mark ready and wait on sense
      _threadDoneArray[me] = true;
      System.out.println("first updated true, sense " + mySense );
    } else {
      // wait for prev to finish
      int prevIdx = me + 1;
      System.out.println(me + " is waiting for prev: " + prevIdx);
      while (!_threadDoneArray[prevIdx]) {} // wait until my pred is done.
//XXX Debug      System.out.println("\t\t\t\t\t my prev " + prevIdx + " updated true");

      if ( me == 0 ) { // im last
        // insure all isDone marks are reset.
        for (int i = (_size - 1) ; i > 0 ; i--) { // one thread that works more - but it never waits on the volatile
          System.out.println("last sets " + i + " to false");
          _threadDoneArray[i] = false;
        }
        System.out.println("last returns (sense = " + _sense + " my sense = " + mySense);
        _sense = mySense; // end phase - release all threads
        _threadSense.set(!mySense); // reset my sense for next phase
        System.out.println("last mySense: " + _threadSense.get() + " globalSense: " + mySense);
        return; // last doesn't need to update the isDone array.
        
      } else {
        _threadDoneArray[me] = true;
        System.out.println(me + " updated true, my sense is " + mySense);
      }
    }
    // all but last should:
    while (_sense != mySense) {} // busy-wait

    _threadSense.set(!mySense); // reset my sense for next phase
  }
}


